]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
st/drawing-area: Merge ClutterCanvas to StDrawingArea
authorShmuel Melamud <smelamud@redhat.com>
Tue, 9 Jan 2024 02:51:31 +0000 (04:51 +0200)
committerMarge Bot <marge-bot@gnome.org>
Wed, 17 Jan 2024 09:41:40 +0000 (09:41 +0000)
Since StDrawingArea is the only user of ClutterCanvas, it is possible
to move ClutterCanvas completely out of Mutter to gnome-shell. This
allows to remove another Cairo dependency from Mutter.

This patch merges ClutterCanvas code extracted from Mutter to
StDrawingArea.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3098>

src/st/st-drawing-area.c

index 20b0d6defff4a4135241bb1f97fd5b9de8cbf202..f8d311c7bc185b67460a2eced77afa6d7aa79291 100644 (file)
 typedef struct _StDrawingAreaPrivate StDrawingAreaPrivate;
 struct _StDrawingAreaPrivate {
   cairo_t *context;
+
+  int width;
+  int height;
+  float scale_factor;
+
+  CoglTexture *texture;
+  CoglBitmap *buffer;
+
+  gboolean dirty;
   guint in_repaint : 1;
 };
 
@@ -53,53 +62,52 @@ enum
 
 static guint st_drawing_area_signals [LAST_SIGNAL] = { 0 };
 
-static gboolean
-draw_content (ClutterCanvas *canvas,
-              cairo_t       *cr,
-              int            width,
-              int            height,
-              gpointer       user_data)
+static void
+st_drawing_area_allocate (ClutterActor          *self,
+                          const ClutterActorBox *box)
 {
-  StDrawingArea *area = ST_DRAWING_AREA (user_data);
-  StDrawingAreaPrivate *priv = st_drawing_area_get_instance_private (area);
-
-  priv->context = cr;
-  priv->in_repaint = TRUE;
-
-  cairo_save (cr);
+  StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
+  StDrawingAreaPrivate *priv =
+      st_drawing_area_get_instance_private (ST_DRAWING_AREA (self));
+  ClutterActorBox content_box;
 
-  cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
-  cairo_paint (cr);
+  priv->scale_factor = clutter_actor_get_resource_scale (self);
 
-  cairo_restore (cr);
-  g_signal_emit (area, st_drawing_area_signals[REPAINT], 0);
+  clutter_actor_set_allocation (self, box);
+  st_theme_node_get_content_box (theme_node, box, &content_box);
 
-  priv->context = NULL;
-  priv->in_repaint = FALSE;
+  priv->width = (int)(0.5 + content_box.x2 - content_box.x1);
+  priv->height = (int)(0.5 + content_box.y2 - content_box.y1);
 
-  return TRUE;
+  st_drawing_area_queue_repaint (ST_DRAWING_AREA (self));
 }
 
 static void
-st_drawing_area_allocate (ClutterActor          *self,
-                          const ClutterActorBox *box)
+st_drawing_area_paint_node (ClutterActor     *actor,
+                            ClutterPaintNode *root)
 {
-  StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
-  ClutterContent *content = clutter_actor_get_content (self);
-  ClutterActorBox content_box;
-  int width, height;
-  float resource_scale;
+  StDrawingArea *area = ST_DRAWING_AREA (actor);
+  StDrawingAreaPrivate *priv = st_drawing_area_get_instance_private (area);
+  ClutterPaintNode *node;
 
-  resource_scale = clutter_actor_get_resource_scale (self);
+  if (priv->buffer == NULL)
+    return;
 
-  clutter_actor_set_allocation (self, box);
-  st_theme_node_get_content_box (theme_node, box, &content_box);
+  if (priv->dirty)
+    g_clear_object (&priv->texture);
+
+  if (priv->texture == NULL)
+    priv->texture = cogl_texture_2d_new_from_bitmap (priv->buffer);
 
-  width = (int)(0.5 + content_box.x2 - content_box.x1);
-  height = (int)(0.5 + content_box.y2 - content_box.y1);
+  if (priv->texture == NULL)
+    return;
 
-  clutter_canvas_set_scale_factor (CLUTTER_CANVAS (content), resource_scale);
-  clutter_canvas_set_size (CLUTTER_CANVAS (content), width, height);
+  node = clutter_actor_create_texture_paint_node (actor, priv->texture);
+  clutter_paint_node_set_static_name (node, "Canvas Content");
+  clutter_paint_node_add_child (root, node);
+  clutter_paint_node_unref (node);
+
+  priv->dirty = FALSE;
 }
 
 static void
@@ -113,23 +121,44 @@ st_drawing_area_style_changed (StWidget  *self)
 static void
 st_drawing_area_resource_scale_changed (ClutterActor *self)
 {
+  StDrawingArea *area = ST_DRAWING_AREA (self);
+  StDrawingAreaPrivate *priv = st_drawing_area_get_instance_private (area);
   float resource_scale;
-  ClutterContent *content = clutter_actor_get_content (self);
 
   resource_scale = clutter_actor_get_resource_scale (self);
-  clutter_canvas_set_scale_factor (CLUTTER_CANVAS (content), resource_scale);
+  if (priv->scale_factor != resource_scale)
+    {
+      priv->scale_factor = resource_scale;
+
+      st_drawing_area_queue_repaint (area);
+
+      if (CLUTTER_ACTOR_CLASS (st_drawing_area_parent_class)->resource_scale_changed)
+        CLUTTER_ACTOR_CLASS (st_drawing_area_parent_class)->resource_scale_changed (self);
+    }
+}
+
+static void
+st_drawing_area_finalize (GObject *self)
+{
+  StDrawingAreaPrivate *priv =
+      st_drawing_area_get_instance_private (ST_DRAWING_AREA (self));
 
-  if (CLUTTER_ACTOR_CLASS (st_drawing_area_parent_class)->resource_scale_changed)
-    CLUTTER_ACTOR_CLASS (st_drawing_area_parent_class)->resource_scale_changed (self);
+  g_clear_object (&priv->buffer);
+  g_clear_object (&priv->texture);
+
+  G_OBJECT_CLASS (st_drawing_area_parent_class)->finalize (self);
 }
 
 static void
 st_drawing_area_class_init (StDrawingAreaClass *klass)
 {
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
   ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
   StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
 
+  gobject_class->finalize = st_drawing_area_finalize;
   actor_class->allocate = st_drawing_area_allocate;
+  actor_class->paint_node = st_drawing_area_paint_node;
   widget_class->style_changed = st_drawing_area_style_changed;
   actor_class->resource_scale_changed = st_drawing_area_resource_scale_changed;
 
@@ -145,10 +174,104 @@ st_drawing_area_class_init (StDrawingAreaClass *klass)
 static void
 st_drawing_area_init (StDrawingArea *area)
 {
-  ClutterContent *content = clutter_canvas_new ();
-  g_signal_connect (content, "draw", G_CALLBACK (draw_content), area);
-  clutter_actor_set_content (CLUTTER_ACTOR (area), content);
-  g_object_unref (content);
+  StDrawingAreaPrivate *priv = st_drawing_area_get_instance_private (area);
+
+  priv->width = -1;
+  priv->height = -1;
+  priv->scale_factor = 1.0f;
+}
+
+static void
+st_drawing_area_emit_repaint (StDrawingArea *area)
+{
+  StDrawingAreaPrivate *priv = st_drawing_area_get_instance_private (area);
+  int real_width, real_height;
+  cairo_surface_t *surface;
+  gboolean mapped_buffer;
+  unsigned char *data;
+  CoglBuffer *buffer;
+  cairo_t *cr;
+
+  g_assert (priv->height > 0 && priv->width > 0);
+
+  priv->dirty = TRUE;
+
+  real_width = ceilf (priv->width * priv->scale_factor);
+  real_height = ceilf (priv->height * priv->scale_factor);
+
+  if (priv->buffer == NULL)
+    {
+      CoglContext *ctx;
+
+      ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
+      priv->buffer = cogl_bitmap_new_with_size (ctx,
+                                                real_width,
+                                                real_height,
+                                                COGL_PIXEL_FORMAT_CAIRO_ARGB32_COMPAT);
+    }
+
+  buffer = COGL_BUFFER (cogl_bitmap_get_buffer (priv->buffer));
+  if (buffer == NULL)
+    return;
+
+  cogl_buffer_set_update_hint (buffer, COGL_BUFFER_UPDATE_HINT_DYNAMIC);
+
+  data = cogl_buffer_map (buffer,
+                          COGL_BUFFER_ACCESS_READ_WRITE,
+                          COGL_BUFFER_MAP_HINT_DISCARD);
+
+  if (data != NULL)
+    {
+      int bitmap_stride = cogl_bitmap_get_rowstride (priv->buffer);
+
+      surface = cairo_image_surface_create_for_data (data,
+                                                     CAIRO_FORMAT_ARGB32,
+                                                     real_width,
+                                                     real_height,
+                                                     bitmap_stride);
+      mapped_buffer = TRUE;
+    }
+  else
+    {
+      surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                            real_width,
+                                            real_height);
+
+      mapped_buffer = FALSE;
+    }
+
+  cairo_surface_set_device_scale (surface,
+                                  priv->scale_factor,
+                                  priv->scale_factor);
+
+  priv->context = cr = cairo_create (surface);
+
+  priv->in_repaint = TRUE;
+
+  cairo_save (priv->context);
+  cairo_set_operator (priv->context, CAIRO_OPERATOR_CLEAR);
+  cairo_paint (priv->context);
+  cairo_restore (priv->context);
+
+  g_signal_emit (area, st_drawing_area_signals[REPAINT], 0);
+
+  priv->context = NULL;
+  priv->in_repaint = FALSE;
+
+  cairo_destroy (cr);
+
+  if (mapped_buffer)
+    cogl_buffer_unmap (buffer);
+  else
+    {
+      int size = cairo_image_surface_get_stride (surface) * priv->height;
+      cogl_buffer_set_data (buffer,
+                            0,
+                            cairo_image_surface_get_data (surface),
+                            size);
+    }
+
+  cairo_surface_destroy (surface);
 }
 
 /**
@@ -164,9 +287,18 @@ st_drawing_area_init (StDrawingArea *area)
 void
 st_drawing_area_queue_repaint (StDrawingArea *area)
 {
+  StDrawingAreaPrivate *priv;
+
   g_return_if_fail (ST_IS_DRAWING_AREA (area));
 
-  clutter_content_invalidate (clutter_actor_get_content (CLUTTER_ACTOR (area)));
+  priv = st_drawing_area_get_instance_private (area);
+
+  g_clear_object (&priv->buffer);
+
+  if (priv->width <= 0 || priv->height <= 0)
+    return;
+
+  st_drawing_area_emit_repaint (area);
 }
 
 /**
@@ -224,24 +356,14 @@ st_drawing_area_get_surface_size (StDrawingArea *area,
                                   guint         *height)
 {
   StDrawingAreaPrivate *priv;
-  ClutterContent *content;
-  float w, h, resource_scale;
 
   g_return_if_fail (ST_IS_DRAWING_AREA (area));
 
   priv = st_drawing_area_get_instance_private (area);
   g_return_if_fail (priv->in_repaint);
 
-  content = clutter_actor_get_content (CLUTTER_ACTOR (area));
-  clutter_content_get_preferred_size (content, &w, &h);
-
-  resource_scale = clutter_actor_get_resource_scale (CLUTTER_ACTOR (area));
-
-  w /= resource_scale;
-  h /= resource_scale;
-
   if (width)
-    *width = ceilf (w);
+    *width = priv->width;
   if (height)
-    *height = ceilf (h);
+    *height = priv->height;
 }